package AIEngine;

import java.util.LinkedList;
import java.util.ListIterator;

public class MinMaxEngine {

	private boolean camp;           // which player is represented by the class
	private int maxDepth;
	private int limitMinMax;		// represents the limit beyond which use minmax
    //private NodeComparator nComp;   //comparison of tow nodes by the value
    
    /**
     * Default constructor for the minmaxengine
     *
     * <ul>
     * <li>searchdeep= 4.</li>
     * <li>Player = firstplayer.</
     * li>
     * </ul>
     * @see NodeComparator
     *
     */
    public MinMaxEngine() {
        camp = true;
        maxDepth = 1;
        limitMinMax = 4;
        //nComp = new NodeComparator();
    }
    
    /**
     * Constructor for the minmaxengine by commiting the playerside
     *
     * @see NodeComparator  
     * @param black true = firstplayer, false = secondplayer.
     */
    public MinMaxEngine(boolean black) {
        camp = black;
        maxDepth = 1;
        limitMinMax = 4;
        //nComp = new NodeComparator();
    }
    
    
    /**
     * Getter for camp
     * @return camp
     */
    public boolean getcamp() {
        return camp;
    }
    
    
    /**
     * Checks if the search is supposed to cancel.
     *
     * @param node current position
     * @return <b>true</b> canceling search, <b>false</b> going on search
     */
    public boolean cutOff(Node node) {
    	
    	if(node.getTurnsLeft() <= limitMinMax)
    		maxDepth = limitMinMax;
    		
        if (node.getDepth() >= maxDepth) {
            return true;
        }

        return false;
    }
    
	
    /**
     * getNodes detects all sequence positions for a node
     *
     * @see Node
     * @param node root node, hear starts the search
     * @return childslist a linkedlist filled with all sequence positions for the root node
     */
    public LinkedList getNodes(Node node) {
        LinkedList<Node> childslist = new LinkedList();
        int[][] workplace = new int[6][6];

        if (BasicAI.checkWin(node.getBoard(), node.getCamp()) == true) {
            return null;
        }

        for (int i = 0; i < 6; i++) {
            for (int j = 0; j < 6; j++) {
                if (BasicAI.isEmpty(node.getBoard()[i][j])) {
                    workplace = node.getBoard();

                    workplace = BasicAI.rotate(workplace, 1, 1);
                    childslist.add(new Node(node, new int[]{i, j, 1, 1,}, workplace, node.getTurnsLeft()));
                    workplace = BasicAI.rotate(workplace, 1, -1);

                    workplace = BasicAI.rotate(workplace, 1, -1);
                    childslist.add(new Node(node, new int[]{i, j, 1, -1,}, workplace, node.getTurnsLeft()));
                    workplace = BasicAI.rotate(workplace, 1, 1);

                    workplace = BasicAI.rotate(workplace, 2, -1);
                    childslist.add(new Node(node, new int[]{i, j, 2, -1,}, workplace, node.getTurnsLeft()));
                    workplace = BasicAI.rotate(workplace, 2, 1);

                    workplace = BasicAI.rotate(workplace, 2, -1);
                    childslist.add(new Node(node, new int[]{i, j, 2, -1,}, workplace, node.getTurnsLeft()));
                    workplace = BasicAI.rotate(workplace, 2, 1);

                    workplace = BasicAI.rotate(workplace, 3, -1);
                    childslist.add(new Node(node, new int[]{i, j, 3, -1,}, workplace, node.getTurnsLeft()));
                    workplace = BasicAI.rotate(workplace, 3, 1);

                    workplace = BasicAI.rotate(workplace, 3, -1);
                    childslist.add(new Node(node, new int[]{i, j, 3, -1,}, workplace, node.getTurnsLeft()));
                    workplace = BasicAI.rotate(workplace, 3, 1);

                    workplace = BasicAI.rotate(workplace, 4, -1);
                    childslist.add(new Node(node, new int[]{i, j, 4, -1,}, workplace, node.getTurnsLeft()));
                    workplace = BasicAI.rotate(workplace, 4, 1);

                    workplace = BasicAI.rotate(workplace, 4, -1);
                    childslist.add(new Node(node, new int[]{i, j, 4, -1,}, workplace, node.getTurnsLeft()));
                    workplace = BasicAI.rotate(workplace, 4, 1);

                }
            }
        }

        return childslist;
    }
    
    
    /**
     * Takes a node and evaluates the given position from it.
     * 
     * BasicAI.checkWin
     * @param node
     * @return value
     */
    public long evalNode(Node node) {
    	
    	node.setTmpboard(node.getBoard());
        long value = 0;

        if (BasicAI.checkWin(node.getTmpboard(), node.getCamp()) == true) {
            value = 99999;
        }
        if (BasicAI.checkWin(node.getTmpboard(), !node.getCamp()) == true) {
          value = -99999;
        }
        if(node.getTurnsLeft() > limitMinMax) {
        	BasicAI.fullJudge(node.getTmpboard(), node.getCamp());
        	node.setValue(BasicAI.getfullValue(node.getTmpboard(), node.getCamp()));
        }
        node.setValue(node.getValue() + value);
        value = node.getValue();
        return value;
    }
    
    
    /**
     * Setter for the depth
     * @param depth new value of depth
     */
    public void setDepth(int depth) {
        this.maxDepth = depth;
    }

    /**
     * Emits the best next possible move, depending on the root node
     *
     * @see Node
     * @param node root for the calculation of the worst following move
     * @return next best move
     */
    public long maxNode(Node node) {
    	long v = Long.MIN_VALUE;
        LinkedList childs;
        ListIterator iterator;
        Node tmpNode = null;
        long  value;

        childs = getNodes(node);

        if (childs == null) {
            return evalNode(node);
        }

        iterator = childs.listIterator(0);
        while (iterator.hasNext()) {
            tmpNode = (Node) iterator.next();
            
            value =  minNode(tmpNode);

            if(value>=v)
            	v = value;
        }

        return v;
    }

    /**
     * Emits the worst next possible move, depending on the root node
     *
     * @see Node
     * @param node root for the calculation of the worst following move
     * @return worst next possible move
     */
    public long minNode(Node node) {
    	long v = Long.MAX_VALUE;
        LinkedList childs;
        ListIterator iterator;
        Node tmpNode = null;
        long value;

        if (cutOff(node)) {
            return evalNode(node);
        }
        
        childs = getNodes(node);

        if (childs == null) {
            return evalNode(node);
        }

        iterator = childs.listIterator(0);
        while (iterator.hasNext()) {
            tmpNode = (Node) iterator.next();
            
            value =  maxNode(tmpNode);

            if(value<=v)
            	v = value;
        }

        return v;

    }


    /**
     * Searchs with the alpha-beta pruning a preferably and maybe best move.
     * Depending on the searchdepth and the timelimit
     *
     * @param node basic position for the search
     * @return Node, the best move is returned as a node and <b>null</b> will
     * be returned if ther is no next move or we don't findet it
     */
    public Node getBestMove(Node node) {
        LinkedList childs;
        ListIterator iterator;
        Node tmpNode;
        Node bestNode;
        long v;

        this.camp = node.getCamp();
        
        v = Long.MIN_VALUE;
        bestNode = null;
        childs = getNodes(node);
        if (childs == null) {
        	return null;
        }

        iterator = childs.listIterator(0);
        while (iterator.hasNext()) {
        	tmpNode = (Node) iterator.next();
            tmpNode.setValue(minNode(tmpNode));

            if (tmpNode.getValue() > v) {
            	v = tmpNode.getValue();
                bestNode = tmpNode;
            }
        }

        bestNode.setDepth(0);
        
        return bestNode;

    }
}
